#ifndef MAP_H_
#define MAP_H_

#include <map>
#include <stdint.h>
#include <string>
#include "FileInterface.h"
#include "MapReader.h"
#include "MapRouter.h"
#include "Route.h"
#include "Road.h"
#include "Geo.h"
#include "Location.h"

namespace opennav {

struct TableSection {
	int32_t start;
	int32_t count;
	int32_t at(int32_t n) const {
		if (n >= count || n < 0) {
			return -1;
		}
		return start + n;
	}
};

struct MultiTableSections {
	std::vector<TableSection> sections;
	int32_t at(int32_t n) const {
		int32_t start, c = 0;
		uint32_t i = 0;
		do {
			if (i >= sections.size()) {
				return -1;
			}
			n -= c;
			c = sections.at(i).count;
			start = sections.at(i).start;
			i++;
		} while ((n - c) >= 0);
		return start + n;
	}
	int32_t count() const {
		int32_t count = 0;
		for (std::vector<TableSection>::const_iterator it = sections.begin(); it
				!= sections.end(); it++) {
			count += (*it).count;
		}
		return count;
	}
};

struct SearchCountryResult {
	TableSection range;
	int32_t count() const {
		return range.count;
	}
};

struct SearchCityResult {
	TableSection range;
	int32_t count() const {
		return range.count;
	}
};

struct SearchRoadResult {
	MultiTableSections range;
	int32_t count() const {
		return range.count();
	}
};

class Map;

struct MapSelection {
	Map* map;
	Area bounds;
	std::vector<MapObject*> items;
};

class MapReader;
class MapTileDef;
class MapRouter;
class RoutingSettings;
class Route;

class Map {
public:
	Map(FileInterface* input, bool enablecache);
	~Map();

	void create_selection(const Area& bounds, uint8_t lod,
			MapSelection& map_selection);
	const std::string get_object_label(MapObject* obj);

	void search_country(const std::string& name, SearchCountryResult& result);
	void get_result(int32_t n, SearchCountryResult& result, Country& country);
	void search_city(const std::string& name, uint8_t country_id,
			SearchCityResult& result);
	void get_result(int32_t n, SearchCityResult& result, City& city);
	void search_road(const std::string& name, int32_t city_id,
			SearchRoadResult& result);
	void get_result(int32_t n, SearchRoadResult& result, Road& road);

	void country(uint8_t country_id, Country& country);
	void region(int16_t region_id, Region& region);
	void city(int32_t city_id, City& city);

	void get_road(const RoadSegment& segment, Road& road);

	bool reverse_geocode(const Coordinate& coordinate, Road& road, RoadSegment& segment, RoadJunction& next_junction, Coordinate& c);
	bool geocode(const Road& road, Coordinate& coordinate);
	bool geocode(const City& city, Coordinate& coordinate);

	void get_segment(int32_t offset, RoadSegment& segment);
	void get_junction(int32_t offset, RoadJunction& junction);

	bool get_map_object(const MapObjectReference& reference, MapLine& object);
	bool get_map_object(const MapObjectReference& reference, MapPoint& object);
	bool get_map_object(const MapObjectReference& reference, MapShape& object);

	void set_routing_settings(const RoutingSettings& settings);
	void reset_route();
	void delete_route();
	const Route* get_route() const;
	bool calculate_route(const RoadJunction& src, const RoadJunction& dst);
	bool calculate_route(const Coordinate& src, const Coordinate& dst);
private:
	bool calculate_route(const RoadJunction& src, const RoadJunction& dst, Route& route);
	void select_tiles(const Area& bounds, uint8_t zoom_level, uint16_t from,
			std::vector<MapTileDef>& tile_defs);
	uint8_t get_zoom_level(const uint8_t lod);
	void get_tile_content(const MapObjectReference& reference, std::vector<MapObject*>*& tile_content);
	void get_tile_content(const MapTileDef& tile_def, std::vector<MapObject*>*& tile_content);
	void flush_tilecache();

private:
	FileInterface* input_;
	MapReader* reader_;
	MapRouter* router_;
	Route* route_;

	typedef std::vector<opennav::MapTileDef> tiledefs;
	tiledefs tiledefs_;
	typedef std::vector<opennav::Zoom> zoomlevels;
	zoomlevels zoomlevels_;
	typedef std::map<uint16_t, std::vector<opennav::MapObject*>*> tilecache;
	tilecache tilecache_;
	bool enablecache_;

	typedef std::map<int32_t, std::string> labelcache;
	labelcache labelcache_;
};

}

#endif /* MAP_H_ */
